package ytr.android.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import ytr.android.sparrow.ScoreManagementPopup;

import android.util.Log;

/**
 * Javaコレクション・フレームワーク Listインタフェースにおいて、
 * 計算処理を提供するユーティリティクラス。
 * @author toki
 */
public class ListCalculation {

	/*
	 * データ型整数を表す定数
	 */
	private static final byte TYPE_INT = 0;

	/*
	 * データ型小数点数を表す定数
	 */
	private static final byte TYPE_DOUBLE = 1;

	/*
	 * データ型文字列を表す定数
	 */
	private static final byte TYPE_STRING = 2;

	/*
	 * 符号付き整数を判定するための正規表現パターン
	 */
	private final static Pattern m_patternInt = Pattern.compile("^[-,+]?[0-9]*");

	/*
	 * 符号付き小数点数を判定するための正規表現パターン
	 */
	private final static Pattern m_patternDouble = Pattern.compile("^[-,+]?[0-9]+[.][0-9]*");
	
	/*
	 * 正規表現によるマッチング処理用オブジェクト
	 */
	private static Matcher m_matcher;
	
	/*
	 * データ型を取得する
	 * 次の優先度で計算式を決める
	 * 整数 < 小数点 < その他(文字列)
	 */
	static private byte getDataType(List<String> list) {
		byte ret = 0;

		for (int i=0 ; i < list.size() ; i++) {
			m_matcher = m_patternInt.matcher(list.get(i));
			if (m_matcher.find()) {
				m_matcher = m_patternDouble.matcher(list.get(i));
				if (m_matcher.find() && ret <= TYPE_DOUBLE) {
					ret = TYPE_DOUBLE;
				} else if (ret <= TYPE_INT){
					ret = TYPE_INT;
				}
			} else {
				ret = TYPE_STRING;
			}
		}
		return ret;
	}
	
	/**
	 * 計算対象を合計して結果を返却する。
	 * @param list 計算対象
	 * @return 計算結果を返却する。エラー時はNullを返却する。
	 */
	public String sumColumns(List<String> list) {

		String ret = new String("");
		
		byte type = getDataType(list);
		
		switch (type) {
		case TYPE_INT:
			ret = sumColumnsInt(list);
			break;
		case TYPE_DOUBLE:
			ret = sumColumnsDouble(list);
			break;
		case TYPE_STRING:
			break;
		default:
			// I am a bug.
		}

		return ret;
	}
	
	private String sumColumnsInt(List<String> list) {
		int tmp = 0;

		try{
			for(int i=0 ; i < list.size() ; i++) {
				tmp += Integer.parseInt(list.get(i));
			}
		} catch(NumberFormatException e) {
			return null;
		}

		return String.format("%d", tmp);
	}

	private String sumColumnsDouble(List<String> list) {
		double tmp = 0;

		try{
			for(int i=0 ; i < list.size() ; i++) {
				tmp += Double.parseDouble(list.get(i));
			}
		} catch(NumberFormatException e) {
			return null;
		}

		return String.format("%f", tmp);
	}
	
	/**
	 * 計算対象の同要素数同士を合計して結果を返却する。
	 * @param list 計算対象
	 * @return 計算結果を返却する。エラー時はNullを返却する。
	 */
	public List<String> sumBetweenColumns(List< List<String> > list) {
		List<String> ret = new ArrayList<String>();

		/*
		 * 複数列が対象の場合、次の優先度で計算式を決める
		 * 整数 < 小数点 < その他(文字列)
		 */
		byte type = 0;
		for (int i=0 ; i < list.size() ; i++) {
			byte tmp = getDataType(list.get(i));
			if (tmp > type) {
				type = tmp;
			}
		}
		
		switch (type) {
		case TYPE_INT:
			ret = sumBetweenColumnsInt(list);
			break;
		case TYPE_DOUBLE:
			ret = sumBetweenColumnsDouble(list);
			break;
		case TYPE_STRING:
			break;
		default:
			// I am a bug.
		}

		return ret;
	}
	
	private List<String> sumBetweenColumnsInt(List< List<String> > list) {
		List<String> ret = new ArrayList<String>();
		List<Integer> calcList = new ArrayList<Integer>();

		/*
		 * for calculation default value
		 */
		int indexmax = 0;
		for (int i=0 ; i < list.size() ; i++) {
			if (indexmax < list.get(i).size()) {
				indexmax = list.get(i).size();
			}
		}
		for (int j=0 ; j < indexmax ; j++) {
			calcList.add((int) 0);
		}
		
		try{			
			for (int i=0 ; i < list.size() ; i++) {
				List<String> tmp = list.get(i);
				
				for (int k=0 ; k < tmp.size() ; k++) {
					calcList.set(k, calcList.get(k) + Integer.parseInt(tmp.get(k)));
				}
			}
		} catch(NumberFormatException e) {
			return null;
		}

		for (int i=0 ; i < calcList.size() ; i++) {
			ret.add(String.format("%d", calcList.get(i)));
		}
		
		return ret;
	}
	
	private List<String> sumBetweenColumnsDouble(List< List<String> > list) {
		List<String> ret = new ArrayList<String>();
		List<Double> calcList = new ArrayList<Double>();

		try{
			for (int i=0 ; i < list.size() ; i++) {
				List<String> tmp = list.get(i);
				
				/*
				 * for calculation default value
				 */
				for (int j=0 ; j < tmp.size() ; j++) {
					calcList.add((double) 0);
				}
				
				for (int k=0 ; k < tmp.size() ; k++) {
					calcList.set(k, calcList.get(k) + Double.parseDouble(tmp.get(k)));
				}
			}
		} catch(NumberFormatException e) {
			return null;
		}

		for (int i=0 ; i < calcList.size() ; i++) {
			ret.add(String.format("%f", calcList.get(i)));
		}
		
		return ret;
	}

	/**
	 * 計算対象の各要素に対して、乗数を用いて計算する。
	 * @param list 計算対象
	 * @param multiplier 乗数
	 * @return 計算結果を返却する。エラー時はNullを返却する。
	 */
	public List<String> multiplicationColumns(List<String> list, int multiplier) {
		List<String> ret = new ArrayList<String>();

		byte type = getDataType(list);
		
		switch (type) {
		case TYPE_INT:
			ret = multiplicationColumnsInt(list, multiplier);
			break;
		case TYPE_DOUBLE:
			ret = multiplicationColumnsDouble(list, multiplier);
			break;
		case TYPE_STRING:
			break;
		default:
			// I am a bug.
		}

		return ret;
		
	}
	
	private List<String> multiplicationColumnsInt(List<String> list, int multiplier) {
		List<String> ret = new ArrayList<String>();
		List<Integer> calcList = new ArrayList<Integer>();

		/*
		 * for calculation default value
		 */
		for (int i=0 ; i < list.size() ; i++) {
			calcList.add((int) 0);
		}

		try{
			for (int i=0 ; i < list.size() ; i++) {
				calcList.set(i, Integer.parseInt(list.get(i)) * multiplier);
			}
		} catch(NumberFormatException e) {
			return null;
		}

		for (int i=0 ; i < calcList.size() ; i++) {
			ret.add(String.format("%d", calcList.get(i)));
		}

		return ret;		
	}

	private List<String> multiplicationColumnsDouble(List<String> list, int multiplier) {
		List<String> ret = new ArrayList<String>();
		List<Double> calcList = new ArrayList<Double>();

		/*
		 * for calculation default value
		 */
		for (int i=0 ; i < list.size() ; i++) {
			calcList.add((double) 0);
		}

		try{
			for (int i=0 ; i < list.size() ; i++) {
				calcList.set(i, Double.parseDouble(list.get(i)) * multiplier);
			}
		} catch(NumberFormatException e) {
			return null;
		}

		for (int i=0 ; i < calcList.size() ; i++) {
			ret.add(String.format("%f", calcList.get(i)));
		}

		return ret;
	}
	
	/**
	 * 文字列リストを数値リストに変換する。
	 * @param list 文字列リスト
	 * @return
	 */
	public List<Integer> convertList_StringToInteger(List<String> list) {
		List<Integer> ret = new ArrayList<Integer>();
		for (int i=0 ; i < list.size() ; i++) {
			ret.add(Integer.parseInt(list.get(i)));
		}
		return ret;
	}

	/**
	 * 数値リストを文字列リストに変換する。
	 * @param list 数値リスト
	 * @return
	 */
	public List<String> convertList_IntegerToString(List<Integer> list) {
		List<String> ret = new ArrayList<String>();
		for (int i=0 ; i < list.size() ; i++) {
			ret.add(String.format("%d", list.get(i)));
		}
		return ret;
	}

	/**
	 * 引数で渡されたリストをランキング付けする。
	 * @param list ランク付け対象リスト
	 * @return
	 */
	public List<String> getScoreRanking(List<String> list, List<String> active) {
		List<String> ret = new ArrayList<String>();

		/*
		 * 降順ソート
		 */
		IntegerComparator comparator = new IntegerComparator(IntegerComparator.DESC);
		List<Integer> tmp = convertList_StringToInteger(list);
		Collections.sort(tmp, (Comparator<Object>)comparator);
		
		for (int i=0 ; i < list.size() ; i++) {
			
			/*
			 * 入力してない場合はランクを付けない
			 */
			if (active.get(i).equals(ScoreManagementPopup.PASSIVE)) {
				continue;
			}
			
			for (int j=0 ; j < tmp.size() ; j++) {
				if (tmp.get(j).equals(Integer.parseInt(list.get(i)) )) {
					ret.add(String.format("%d", j+1)); // +1は要素数からランク数への変換処理
					break;
				}
			}
		}
		
		return ret;
	}

	/**
	 * 数値リストソート用クラス
	 * @author toki
	 */
	class IntegerComparator implements Comparator<Object> {

		/**
		 * 昇順ソート定義値
		 */
		public static final byte ASC = 0;
		
		/**
		 * 降順ソート定義値
		 */
		public static final byte DESC = -1;
		
		/*
		 * ソート方向定義値
		 */
		private byte m_bOrderby;		
		
		/**
		 * コンストラクタ
		 * @param orderby ソート方向指定(ASC or DESC)
		 */
		public IntegerComparator(byte orderby) {
			m_bOrderby = orderby;
		}

		/**
		 * 
		 */
		@Override
		public int compare(Object lhs, Object rhs) {
			return ((Integer)lhs).compareTo((Integer)rhs) * m_bOrderby;
		}		
	}
	
	/**
	 * 計算対象の各要素に対して、乗数を用いて計算する。
	 * @param list 計算対象
	 * @param multiplier 乗数
	 * @return 計算結果を返却する。エラー時はNullを返却する。
	 */
	public List<String> divisionColumns(List<String> list, int division) {
		List<String> ret = new ArrayList<String>();

		byte type = getDataType(list);
		
		switch (type) {
		case TYPE_INT:
			ret = divisionColumnsInt(list, division);
			break;
		case TYPE_DOUBLE:
			ret = divisionColumnsDouble(list, division);
			break;
		case TYPE_STRING:
			break;
		default:
			// I am a bug.
		}

		return ret;
		
	}
	
	private List<String> divisionColumnsInt(List<String> list, int division) {
		List<String> ret = new ArrayList<String>();
		List<Integer> calcList = new ArrayList<Integer>();

		/*
		 * for calculation default value
		 */
		for (int i=0 ; i < list.size() ; i++) {
			calcList.add((int) 0);
		}

		try{
			for (int i=0 ; i < list.size() ; i++) {
				calcList.set(i, Integer.parseInt(list.get(i)) / division);
			}
		} catch(NumberFormatException e) {
			return null;
		}

		for (int i=0 ; i < calcList.size() ; i++) {
			ret.add(String.format("%d", calcList.get(i)));
		}

		return ret;		
	}

	private List<String> divisionColumnsDouble(List<String> list, int division) {
		List<String> ret = new ArrayList<String>();
		List<Double> calcList = new ArrayList<Double>();

		/*
		 * for calculation default value
		 */
		for (int i=0 ; i < list.size() ; i++) {
			calcList.add((double) 0);
		}

		try{
			for (int i=0 ; i < list.size() ; i++) {
				calcList.set(i, Double.parseDouble(list.get(i)) / division);
			}
		} catch(NumberFormatException e) {
			return null;
		}

		for (int i=0 ; i < calcList.size() ; i++) {
			ret.add(String.format("%f", calcList.get(i)));
		}

		return ret;
	}	
}
